/*    file: WtlVideoWindow.cpp
 *    desc:
 * 
 * created: 2014-01-21 14:12:16
 *  author: zhengchuanjiang
 * company: 
 */

//#include "stdafx.h"
#include "WtlVideoWindow.h"
#include "Path.h"
#include "FrameToBitmap.h"
#include "TStringUtil.h"
#include <assert.h>

#include "AppProperties.h"
#include "MediaProp.h"
#include "FfmpegUtil.h"

//////////////////////////////////////////////////////////////////////////
static const UINT_PTR TIMER_CONTROL = 1;
static const UINT ELAPSE_CONTROL = 1000 * 4;

static const UINT_PTR TIMER_TRACK = 2;
static const UINT ELAPSE_TRACK = 500;


static void CrossImage(CImage &img)  //对像素进行转换 
{
    for(int i=0; i<img.GetWidth(); i++)
    {
        for(int j=0; j<img.GetHeight(); j++)
        {
            UCHAR *cr = (UCHAR*) img.GetPixelAddress(i,j);
            cr[0] = cr[0]*cr[3] / 255;
            cr[1] = cr[1]*cr[3] / 255;
            cr[2] = cr[2]*cr[3] / 255;
        }
    }
}

//////////////////////////////////////////////////////////////////////////
namespace av
{



WtlVideoWindow::WtlVideoWindow():
    m_parentWnd(),
    m_ratioX(),
    m_ratioY(),
    m_enableControl(),
    m_fullscreen(),
    m_rcLast(),
    m_state(STATE_STOPPED)
{
    HINSTANCE hInstance = ModuleHelper::GetModuleInstance();
    std::string workDir = comn::Path::getWorkDir(hInstance);
    std::string filepath = comn::Path::join(workDir, "logo.png");
    setBackImage(filepath.c_str());

    WNDCLASSEXA& wndClass = GetWndClassInfo().m_wc;
    wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wndClass.hbrBackground = CreateSolidBrush(RGB(0, 0, 100));
}

WtlVideoWindow::~WtlVideoWindow()
{
}


int WtlVideoWindow::open(void* hwnd)
{
    m_parentWnd = (HWND)hwnd;
    subclassWindow(m_parentWnd);

    CRect rcClient;
    ::GetClientRect(m_parentWnd, &rcClient);

    HWND hThis = Create(m_parentWnd, &rcClient);

    //DWORD dwStyle = ::GetWindowLong(m_hWnd, GWL_STYLE);
    //dwStyle = dwStyle | WS_CLIPCHILDREN;
    //::SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);
    
    checkShowControl();
    updateUI(STATE_STOPPED);

    bool showClock = false;
    AppProperties::getProperties().getBool(MEDIA_VIDEO_PROP_SHOW_CLOCK, showClock);
    if (showClock)
    {
        m_presenter.enableShowFrameInfo(true);
    }

    return 0;
}

void WtlVideoWindow::close()
{
    unsubclassWindow(m_parentWnd);

    if (IsWindow())
    {
        DestroyWindow();
    }

    m_parentWnd = NULL;

    clearBuffer();
}

bool WtlVideoWindow::isOpen()
{
    return (TRUE == ::IsWindow(m_hWnd));
}

void WtlVideoWindow::setVideoWnd(void* hwnd)
{
    unsubclassWindow(m_parentWnd);

    m_parentWnd = (HWND)hwnd;

    subclassWindow(m_parentWnd);

    SetParent(m_parentWnd);

    CRect rc;
    ::GetClientRect(m_parentWnd, &rc);
    MoveWindow(rc, TRUE);
}


void WtlVideoWindow::setState(StreamState state)
{
    comn::AutoCritSec lock(m_cs);

    m_state = state;

    ::PostMessage(m_hWnd, WM_SET_STATE, state, 0);
}

bool WtlVideoWindow::draw(AVFrame* frame)
{
    if (m_state == STATE_STOPPED)
    {
        return false;
    }

//    m_presenter.draw(m_hWnd, frame.get(), m_ratioX, m_ratioY);
//    return 0;

	{
		comn::AutoCritSec lock(m_cs);
		m_backFrame = ffmpeg::makeFrame(frame);
	}
    
    Invalidate(FALSE);

    return 0;
}


bool WtlVideoWindow::isFullscreen()
{
    return m_fullscreen;
}

bool WtlVideoWindow::setFullscreen(bool full)
{
    if (full == m_fullscreen)
    {
        return false;
    }

    putFullScreen(full);

    m_fullscreen = full;

    checkShowControl();

    if (!m_fullscreen)
    {
        stopControlTimer();
    }

    return 0;
}

bool WtlVideoWindow::setRatio(int width, int height)
{
    {
        comn::AutoCritSec lock(m_cs);
        m_ratioX = width;
        m_ratioY = height;
    }

    Invalidate(TRUE);

    return true;
}

void WtlVideoWindow::setVisible(bool visible)
{
	int cmd = visible ? SW_SHOW : SW_HIDE;
	ShowWindow(cmd);
}

void WtlVideoWindow::getRatio(int& width, int& height)
{
    width = m_ratioX;
    height = m_ratioY;
}

bool WtlVideoWindow::setBackImage(const char* filepath)
{
    if (!m_backImage.IsNull())
    {
        m_backImage.Destroy();
    }

    HRESULT hr = m_backImage.Load(filepath);
	if (FAILED(hr))
	{
		return false;
	}
	
	CrossImage(m_backImage);
    
    return true;
}

void WtlVideoWindow::flush()
{
    {
        comn::AutoCritSec lock(m_cs);
        m_backFrame.reset();
        m_curFrame.reset();
    }

    m_presenter.reset();
}


void WtlVideoWindow::drawBackImage(HDC hdc)
{
    CRect rc;
    ::GetClientRect(m_hWnd, &rc);
    ::FillRect(hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));

    if (m_backImage.IsNull())
    {
        return;
    }

    int imageWidth = m_backImage.GetWidth();
    int imageHeight = m_backImage.GetHeight();

    int x = (rc.Width() - imageWidth) / 2;
    int y = (rc.Height() - imageHeight) / 2;
    m_backImage.Draw(hdc, x, y);

}

void WtlVideoWindow::drawBackground(HDC hdc)
{
    RECT rc;
    ::GetClientRect(m_hWnd, &rc);

    HBRUSH hBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);

    CRect rcVideo = getVideoRect();
    if (rcVideo.IsRectEmpty())
    {
        ::FillRect(hdc, &rc, hBrush);
    }
    else
    {
        CRect rcTop(rc);
        rcTop.bottom = rcVideo.top;
        ::FillRect(hdc, &rcTop, hBrush);

        CRect rcLeft(rc);
        rcLeft.right = rcVideo.left;
        ::FillRect(hdc, &rcLeft, hBrush);

        CRect rcRight(rc);
        rcRight.left = rcVideo.right;
        ::FillRect(hdc, &rcRight, hBrush);

        CRect rcBottom(rc);
        rcBottom.top = rcVideo.bottom;
        ::FillRect(hdc, &rcBottom, hBrush);
    }

    //HBRUSH brush = CreateSolidBrush(RGB(255, 0, 0));
    //::FrameRect(hdc, &rc, brush);
    //::DeleteObject(brush);
}

void WtlVideoWindow::drawFrame()
{
	fetchFrame();

	comn::AutoCritSec lock(m_cs);
	if (m_curFrame)
	{
		m_presenter.draw(m_hWnd, m_curFrame.get(), m_ratioX, m_ratioY);
	}
	else
	{
		HDC hdc = GetWindowDC();
		drawBackImage(hdc);
		ReleaseDC(hdc);
	}
}

static BOOL screenToClient(HWND hwnd, RECT& rc)
{
    POINT ptTop = {rc.left, rc.top};
    POINT ptBottom = {rc.right, rc.bottom};
    BOOL done = ::ScreenToClient(hwnd, &ptTop);
    ::ScreenToClient(hwnd, &ptBottom);
    rc.left = ptTop.x;
    rc.top = ptTop.y;
    rc.right = ptBottom.x;
    rc.bottom = ptBottom.y;
    return done;
}

static BOOL moveWindow(HWND hwnd, const RECT& rc, BOOL bRepaint)
{
    return MoveWindow(hwnd, rc.left, rc.top,
        rc.right - rc.left,
        rc.bottom - rc.top, bRepaint);
}

void WtlVideoWindow::putFullScreen(bool full)
{
    if (full)
    {
        ::GetWindowRect(m_hWnd, &m_rcLast);
        screenToClient(m_parentWnd, m_rcLast);

        RECT rc;
        ::GetWindowRect(m_hWnd, &rc);

        HMONITOR  hMonitor = MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST);
        MONITORINFO mi;
        mi.cbSize = sizeof(mi);
        GetMonitorInfo(hMonitor, &mi);
        RECT rcFull = mi.rcMonitor;
        //TRACE("rcFull. %d,%d,%d,%d\n", rcFull.left, rcFull.top, rcFull.right, rcFull.bottom);

        HWND hDesktop = ::GetDesktopWindow();
        ::SetParent(m_hWnd, hDesktop);

        //rcFull.right = 400;
        //rcFull.bottom = 400;

        moveWindow(m_hWnd, rcFull, TRUE);
        ::SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
    }
    else
    {
        ::SetParent(m_hWnd, m_parentWnd);

        if (!::IsRectEmpty(&m_rcLast))
        {
            moveWindow(m_hWnd, m_rcLast, TRUE);
        }
    }
}

void WtlVideoWindow::checkShowControl()
{
    if (m_fullscreen)
    {
        // pass
    }
    else
    {
        int cmd = m_enableControl ? SW_SHOW : SW_HIDE;

        if (m_enableControl && (m_state != STATE_STOPPED))
        {
            startTrackTimer();
        }
        else
        {
            stopTrackTimer();
        }
    }
}

bool WtlVideoWindow::histControl(int x, int y)
{
    CRect rc;
    ::GetClientRect(m_hWnd, &rc);
    return (rc.bottom - y) <= 8;
}

void WtlVideoWindow::startControlTimer()
{
    SetTimer(TIMER_CONTROL, ELAPSE_CONTROL, NULL);
}

void WtlVideoWindow::stopControlTimer()
{
    KillTimer(TIMER_CONTROL);
}

void WtlVideoWindow::onTimerControl()
{
}

void WtlVideoWindow::onCommand(UINT flags, int id, HWND hwnd)
{
    
}

void WtlVideoWindow::onHScroll(UINT code, UINT id)
{
    
}

void WtlVideoWindow::updateUI(int state)
{
}

void WtlVideoWindow::onSetState(int state)
{
    if (state == STATE_PLAYING)
    {
        startTrackTimer();

        drawFrame();
    }
    else if (state == STATE_STOPPED)
    {
        m_presenter.reset();

        Invalidate(TRUE);

        clearBuffer();

        stopTrackTimer();
    }

    updateUI(state);
}


void WtlVideoWindow::startTrackTimer()
{
    SetTimer(TIMER_TRACK, ELAPSE_TRACK, NULL);
}

void WtlVideoWindow::stopTrackTimer()
{
    KillTimer(TIMER_TRACK);

    std::string strTime = "00:00/00:00";
}

void WtlVideoWindow::onTimerTrack()
{
    
}

bool WtlVideoWindow::navigate(const char* url)
{
    DWORD_PTR dwRet = (DWORD_PTR)::ShellExecute(0, _T("open"), url, 0, 0, SW_SHOWNORMAL);
    return (dwRet > 32);
}

void WtlVideoWindow::clearBuffer()
{
    comn::AutoCritSec lock(m_cs);
    m_backFrame.reset();
}


bool WtlVideoWindow::getVideoSize(int& width, int& height)
{
    bool found = false;
    comn::AutoCritSec lock(m_cs);
    if (m_curFrame)
    {
        width = m_curFrame->width;
        height = m_curFrame->height;
        found = true;
    }
    return found;
}

CRect WtlVideoWindow::getVideoRect()
{
    CRect rc(0, 0, 0, 0);
    int picWidth = 0;
    int picHeight = 0;
    if (!getVideoSize(picWidth, picHeight))
    {
        return rc;
    }

    GetClientRect(&rc);
    return HwndPresenter::getDestRect(picWidth, picHeight, 
        rc.Width(), rc.Height(), m_ratioX, m_ratioY);
}


LRESULT WtlVideoWindow::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
    if (!::IsWindow(m_hWnd))
    {
        return 0;
    }

    CPaintDC dc(m_hWnd);

    if (m_state == STATE_PLAYING)
    {
        drawFrame();
    }
    else if (m_state == STATE_PAUSED)
    {
        drawFrame();
    }
    else
    {
        drawBackImage(dc.m_hDC);
    }

    return 0;
}

BOOL WtlVideoWindow::OnEraseBkgnd(HDC hdc)
{
    //AppLog::debug("OnEraseBkgnd\n");
    drawBackground(hdc);

    return TRUE;
}

void WtlVideoWindow::OnSize(UINT nType, const CSize& size)
{
    SetMsgHandled(false);
}

LRESULT WtlVideoWindow::OnCreate(LPCREATESTRUCT lpcs)
{
    return 0;
}

void WtlVideoWindow::OnTimer(UINT_PTR wParam)
{
    if (wParam == TIMER_TRACK)
    {
        onTimerTrack();
    }
    else if (wParam == TIMER_CONTROL)
    {
        onTimerControl();
    }
}

void WtlVideoWindow::OnHScroll(UINT nSBCode, UINT nPos, HWND hwnd)
{
    int id = ::GetDlgCtrlID(hwnd);
    onHScroll(nSBCode, id);
}

void WtlVideoWindow::OnCommand(UINT flags, int id, HWND hwnd)
{
    onCommand(flags, id, hwnd);
}

LRESULT WtlVideoWindow::OnSetState(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    bHandled = TRUE;

    onSetState(wParam);

    return 0;
}

void WtlVideoWindow::OnLButtonDblClk(UINT flags, CPoint point)
{
    if (AppProperties::getProperties().getBool(MEDIA_VIDEO_PROP_ENABLE_FULLSCREEN, false))
    {
        setFullscreen(!m_fullscreen);
    }

	WPARAM wparam = flags;
	LPARAM lparam = MAKELPARAM(point.x, point.y);
	::PostMessage(m_parentWnd, WM_LBUTTONDBLCLK, wparam, lparam);
}

void WtlVideoWindow::OnLButtonDown(UINT flags, CPoint point)
{

	WPARAM wparam = flags;
	LPARAM lparam = MAKELPARAM(point.x, point.y);
	::PostMessage(m_parentWnd, WM_LBUTTONDOWN, wparam, lparam);
}

void WtlVideoWindow::OnLButtonUp(UINT flags, CPoint point)
{

	WPARAM wparam = flags;
	LPARAM lparam = MAKELPARAM(point.x, point.y);
	::PostMessage(m_parentWnd, WM_LBUTTONUP, wparam, lparam);
}

void WtlVideoWindow::OnRButtonDown(UINT flags, CPoint point)
{
	WPARAM wparam = flags;
	LPARAM lparam = MAKELPARAM(point.x, point.y);
	::PostMessage(m_parentWnd, WM_RBUTTONDOWN, wparam, lparam);
}

void WtlVideoWindow::OnRButtonUp(UINT flags, CPoint point)
{

	WPARAM wparam = flags;
	LPARAM lparam = MAKELPARAM(point.x, point.y);
	::PostMessage(m_parentWnd, WM_RBUTTONUP, wparam, lparam);
}

void WtlVideoWindow::OnMouseMove(UINT flags, CPoint point)
{
    if (m_fullscreen)
    {
    }
}



void WtlVideoWindow::onStateChanged(int state)
{

}

void WtlVideoWindow::onPositionChanged(int64_t pos)
{
    // 
}

void WtlVideoWindow::onScaleChanged(double scale)
{

}

void WtlVideoWindow::onVolumeChanged(double volume)
{
}

void WtlVideoWindow::onMuteChanged(bool muted)
{
}

void WtlVideoWindow::onFullscreenChanged(bool full)
{
    // pass
}

void WtlVideoWindow::onRatioChanged(int width, int height)
{

}

LRESULT CALLBACK WtlVideoWindow::MyWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    WtlVideoWindow *pObject = (WtlVideoWindow*)::GetWindowLong(hWnd,GWL_USERDATA);

    switch(message)
    {
    case WM_SIZE:
        {
            if (pObject->IsWindow())
            {
                CRect rc;
                ::GetClientRect(hWnd, &rc);
                pObject->MoveWindow(rc, TRUE);
            }
        }
        break;

    default:
        return ::CallWindowProc(pObject->m_superWindowProc, hWnd, message, wParam, lParam); 
    }
    return 0;
}

void WtlVideoWindow::subclassWindow(HWND hwnd)
{
    if (hwnd == NULL)
    {
        return;
    }

    ::SetWindowLong(hwnd, GWL_USERDATA, (LONG)(void*)this);
    
    m_superWindowProc = (WNDPROC)::SetWindowLong(hwnd, GWL_WNDPROC, (LONG)MyWndProc);
}

void WtlVideoWindow::unsubclassWindow(HWND hwnd)
{
    if (hwnd == NULL)
    {
        return;
    }

    ::SetWindowLong(hwnd, GWL_WNDPROC, (LONG)m_superWindowProc);
    m_superWindowProc = NULL;
}


void WtlVideoWindow::fetchFrame()
{
    comn::AutoCritSec lock(m_cs);
    if (m_backFrame)
    {
		if (m_curFrame)
		{
			if (m_curFrame->width != m_backFrame->width ||
				m_curFrame->height != m_backFrame->height)
			{
				Invalidate(TRUE);
			}
		}

        m_curFrame = m_backFrame;
        m_backFrame.reset();
    }
}



}
